import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DeleteResult, Repository } from 'typeorm';
import { LoginUserDto } from './dto/login-user.dto';
import { UserEntity } from './user.entity';
import { compareSync } from 'bcryptjs'
import { CreateUserDto } from './dto/create-user.dto';
import { UserRO } from './user.interface';
import { validate } from 'class-validator';
import { UpdateUserDto } from './dto/update-user.dto';
const jwt = require('jsonwebtoken')
import { SECRET } from '../config/secret'


@Injectable()
export class UserService {
  constructor(
    @InjectRepository(UserEntity)
    private readonly userRepository: Repository<UserEntity>
  ) {}

  async findAll(): Promise<UserEntity[]> {
    return await this.userRepository.find()
  }

  async findOne({ email, password }: LoginUserDto): Promise<UserEntity> {
    const user = await this.userRepository.findOneBy({ email })
    if (!user) {
      return null
    }

    if (compareSync(password, user.password)) {
      return user
    }

    return null
  }

  async create(dto: CreateUserDto): Promise<UserRO> {
    const { username, email, password } = dto
    // const user = await this.userRepository
    //   .createQueryBuilder('user')
    //   .where('user.username = :username', { username })
    //   .orWhere('user.email = :email', { email })
    //   .getOne();
    let user = await this.userRepository
      .createQueryBuilder('user')
      .where('user.username = :username', { username })
      .getOne()

    if (user) {
      const errors = { username: '用户名必须唯一' }
      throw new HttpException({ message:'数据验证失败', errors, code: 409 }, HttpStatus.BAD_REQUEST)
    }

    user = await this.userRepository
    .createQueryBuilder('user')
    .where('user.email = :email', { email })
    .getOne()

    if (user) {
      const errors = { email: '邮箱必须唯一' }
      throw new HttpException({ message:'数据验证失败', errors, code: 409 }, HttpStatus.BAD_REQUEST)
    }

    const newUser = new UserEntity()
    newUser.username = username
    newUser.email = email
    newUser.password = password

    const errors = await validate(newUser)
    if (errors.length > 0) {
      const _errors = '用户输入不合法'
      throw new HttpException({ message: '数据验证失败', _errors, code: 401  }, HttpStatus.BAD_REQUEST)
    } else {
      const savedUser = await this.userRepository.save(newUser)
      return this.buildUserRO(savedUser)
    }
  }

  async findById(id: number): Promise<UserRO> {
    const user = await this.userRepository.findOneBy({ id })

    if (!user) {
      const errors = '无此用户'
      throw new HttpException({ message: '获取用户失败', errors, code: 401 }, HttpStatus.BAD_REQUEST)
    }

    return this.buildUserRO(user)
  }

  async findByEmail(email: string): Promise<UserRO> {
    const user = await this.userRepository.findOneBy({ email })

    if (!user) {
      const errors = '无此用户'
      throw new HttpException({ message: '获取用户失败', errors, code: 401 }, HttpStatus.BAD_REQUEST)
    }

    return this.buildUserRO(user)
  }

  async update(id: number, dto: UpdateUserDto): Promise<UserEntity> {

    const { username, email } = dto

    let user = await this.userRepository
      .createQueryBuilder('user')
      .where('user.username = :username', { username })
      .andWhere('user.id <> :id', { id })
      .getOne()

    if (user) {
      const errors = { username: '用户名已存在' }
      throw new HttpException({ message:'数据验证失败', errors, code: 409 }, HttpStatus.BAD_REQUEST)
    }

    user = await this.userRepository
    .createQueryBuilder('user')
    .where('user.email = :email', { email })
    .andWhere('user.id <> :id', { id })
    .getOne()

    if (user) {
      const errors = { email: '邮箱已存在' }
      throw new HttpException({ message:'数据验证失败', errors, code: 409 }, HttpStatus.BAD_REQUEST)
    }

    const toUpdate = await this.userRepository.findOneBy({ id })
    if (!toUpdate) {
      const errors = '无此用户'
      throw new HttpException({ message: '修改用户失败', errors, code: 401 }, HttpStatus.BAD_REQUEST)
    }

    delete toUpdate.password

    const updatedUser = Object.assign(toUpdate, dto)
    return await this.userRepository.save(updatedUser)
  }

  async remove (id: number): Promise<Number> {
    const user = await this.userRepository.findOneBy({ id })

    if (!user) {
      const errors = '无此用户'
      throw new HttpException({ message: '删除用户失败', errors, code: 401 }, HttpStatus.BAD_REQUEST)
    }

    const { affected } = await this.userRepository.softDelete({ id })
    return affected
  }

  public generateJWT (user) {
    const today = new Date()
    const exp = new Date(today)
    exp.setDate(today.getDate() + 60)

    return jwt.sign({
      id: user.id,
      username: user.username,
      email: user.email,
      exp: exp.getTime() / 1000
    }, SECRET)
  }

  private buildUserRO (user: UserEntity) {
    const userRO = {
      id: user.id,
      username: user.username,
      email: user.email,
      token: this.generateJWT(user),
      bio: user.bio,
      image: user.image
    }

    return { user: userRO }
  }
}
